package cn.yx.common.security.webmvc.sms;

import cn.hutool.core.util.StrUtil;
import cn.yx.common.redis.service.RedisService;
import cn.yx.common.security.constants.SecurityRedisCacheConstant;
import cn.yx.common.security.pojo.JwtUserDetails;
import cn.yx.common.security.service.JwtUserDetailsService;
import cn.yx.common.security.webmvc.constant.message.SmsCodeAuthenticationProviderMessageKey;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;

/**
 * <p>手机验证码验证器，先判断手机号是否正确，如果手机号正确则用户存在，
 * 再判断用户输入的手机验证码是否正确，如果不正确，则抛出自定义的异常信息，
 * 如果手机验证码正确，则验证通过，
 * 验证通过，把JwtUserDetails放入AuthenticationToken中，
 * 最终在CustomAuthenticationSuccessHandler中获取此JwtUserDetails并返回jwt token</p>
 *
 * @author Wgssmart
 */
@Component
public class SmsCodeAuthenticationProvider implements AuthenticationProvider {

    private final JwtUserDetailsService jwtUserDetailsService;

    private final RedisService redisService;

    public SmsCodeAuthenticationProvider(JwtUserDetailsService jwtUserDetailsService, RedisService redisService) {
        this.jwtUserDetailsService = jwtUserDetailsService;
        this.redisService = redisService;
    }

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        SmsCodeAuthenticationToken authenticationToken = (SmsCodeAuthenticationToken) authentication;
        // SmsCodeAuthenticationToken中的principal为手机号
        String phone = (String) authenticationToken.getPrincipal();
        JwtUserDetails jwtUserDetails = (JwtUserDetails) jwtUserDetailsService.loadUserByUsername(phone);
        if (StrUtil.isEmpty(jwtUserDetails.getUsername())) {
            throw new UsernameNotFoundException(SmsCodeAuthenticationProviderMessageKey.PHONE_NOT_EXIST);
        }
        String smsCode = (String) authenticationToken.getCredentials();
        checkCode(phone, smsCode);
        SmsCodeAuthenticationToken authenticationResult = new SmsCodeAuthenticationToken(jwtUserDetails, jwtUserDetails.getAuthorities());
        authenticationResult.setDetails(authenticationToken.getDetails());
        return authenticationResult;
    }

    /**
     * 校验验证码
     *
     * @param phone
     * @param smsCode
     */
    private void checkCode(String phone, String smsCode) {
        String smsCodeInRedis = (String) redisService.get(SecurityRedisCacheConstant.SMS_CODE_LOGIN_PREFIX + phone);
        if (StrUtil.isEmpty(smsCodeInRedis)) {
            // 短信验证码不存在
            throw new SmsCodeExpirationException(SmsCodeAuthenticationProviderMessageKey.SMS_CODE_NOT_EXIST);
        }
        if (!StrUtil.equals(smsCode, smsCodeInRedis)) {
            // 短信验证码错误
            throw new SmsCodeErrorException(SmsCodeAuthenticationProviderMessageKey.SMS_CODE_ERROR);
        }
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return SmsCodeAuthenticationToken.class.isAssignableFrom(authentication);
    }

}